#include <stdio.h>
#include <stdlib.h>

#include <new>
#include <string>
#include <ios>
#include <iostream>
#include <vector>
#include <map>
#include <stdexcept>
#include <iostream>
#include <cstdlib>

#include <glib.h>

#include <assert.h>

#include <mysql.h>
#include <glib.h>

#include <config.h>

#include "ServerInfo.h"
#include "HL2ServerInfo.h"
#include "HLServerInfo.h"

#include "GBConfig.h"

#include "db.h"

#ifdef HAVE_LIBCGICC
#include <cgicc/Cgicc.h>
#include <cgicc/HTTPHTMLHeader.h>
#include <cgicc/HTMLClasses.h>
using namespace cgicc;
#else
extern "C" {
#include <cgi.h>
};
#endif

static MYSQL conn;

using namespace std;

#define LAT_KM_RATIO 111.2
#define LON_KM_RATIO 70.0

void error(const char *msg);
char *sanitize(const char *);
char *coerceToUtf8(const char *p);

int main(int argc, char **argv)
{
	try {
		char sql[2048];
		MYSQL_RES *sth;
		const char *cfgfile = "/etc/gamebrowser.cfg";
		std::string dbh, dbn, dbu, dbp;
		std::string ip;
		int port = 0;
		char *qip = NULL;
		std::map<string, string> dbinfo;
		ServerInfo *si = NULL;
#ifdef HAVE_LIBCGICC
		Cgicc cgi;
#else
		s_cgi *cgi;
		cgi = cgiInit();
#endif

		GBConfig cfg(cfgfile);
		dbh = cfg.getValue("dbhost");
		dbn = cfg.getValue("dbname");
		dbu = cfg.getValue("dbuser");
		dbp = cfg.getValue("dbpass");

		db_setConfig(dbh.c_str(), dbu.c_str(), dbp.c_str(), dbn.c_str());
		// must connect before db_escape
		db_connect(&conn);

#ifdef HAVE_LIBCGICC
		const_form_iterator val = cgi.getElement("port");
		if(val != (*cgi).end() && ! val->isEmpty()) {
			port = (*val).getIntegerValue();
			cerr << "Got port " << port << endl;
		}
		val = cgi.getElement("ip");
		if(val != (*cgi).end() && ! val->isEmpty()) {
			ip = (*val).getValue();
			cerr << "Got ip " << ip << endl;
			qip = db_escape(&conn, ip.c_str());
			cerr << "Got qip " << qip << endl;
			cerr << "aqui " << __LINE__ << endl;
		}
#else
		std::string val = cgiGetValue(cgi, "port");
		if(val.length() > 0) {
			port = atoi(val.c_str());
		}
		ip = cgiGetValue(cgi, "ip");
		if(ip.length() > 0) {
			qip = db_escape(&conn, ip.c_str());
		}
#endif
		if(!qip || !port) {
			error("Missing ip or port");
		}

		snprintf(sql, sizeof(sql), "SELECT server.*, "
				"X(location.location) AS latitude, Y(location.location) AS longitude, "
				"'-DOWN-' AS numplayers "
				"FROM server INNER JOIN location USING (ip) "
				"WHERE ip = '%s' AND port = %i", qip, port
			);

		cerr << "SQL: " << sql << endl;


		if(qip)
			g_free(qip);

#ifdef HAVE_LIBCGICC
		cout << HTTPContentHeader("text/xml; charset=utf-8");
#else
		cgiSetType("text/xml; charset=utf-8");
		cgiHeader();
#endif
		cout << "<?xml version='1.0' encoding=\"utf-8\"?>" << endl;
		cout.setf(ios_base::fixed, ios_base::floatfield);
		cout.precision(10);
		cout << "<serverlist>" << endl;
		sth = db_doSql(&conn, sql);
		if(sth) {
			MYSQL_ROW row;
			MYSQL_FIELD *fields = mysql_fetch_fields(sth);
			int num_fileds = mysql_num_fields(sth);



			while((row = mysql_fetch_row(sth))) {

#ifndef OLD
				for(int i=0;i<num_fileds;i++) {
					dbinfo[fields[i].name] = row[i];
				}
				std::map<string, string> attrs, rules;
				std::map<string, string>::iterator iter;
				if(dbinfo["game"] == "hl") {
					si = new HLServerInfo(ip.c_str(), port);
				} else if(dbinfo["game"] == "hl2") {
					si = new HL2ServerInfo(ip.c_str(), port);
				}
				if(si) {
					if(si->update(1,0)) {
						attrs = si->getAttributes();
						si->updateRules(1,0);
						rules=si->getRules();
					}
					delete(si);
				}
#endif

				cout << "<server>";
				for(int i=0;i<num_fileds;i++) {
					cout << "<" << fields[i].name << "><![CDATA[";
#ifdef OLD
					cout << row[i];
#else
					if((iter = attrs.find(fields[i].name)) != attrs.end() || (iter = rules.find(fields[i].name)) != rules.end()) {
						char *coerced = sanitize(iter->second.c_str());
						cout << coerced;
						g_free(coerced);
					} else {
						cout << row[i];
					}
#endif
					cout << "]]></" << fields[i].name << ">";
				}
				cout << "</server>" << endl;
			}
		}
		cout << "</serverlist>" << endl;
	} catch (const std::exception& e) {
		error(e.what());
	}
	db_close(&conn);
	exit(0);
}

void error(const char *msg)
{
#ifdef HAVE_LIBCGICC
	cout << HTTPContentHeader("text/xml; charset=utf-8");
#else
	cgiSetHeader("Content-Type", "text/xml; charset=utf-8");
	cgiHeader();
#endif
	cout << "<?xml version='1.0' encoding=\"utf-8\"?>" << endl;
	cout << "<error>" << msg << "</error>" << endl;

	exit(0);
}

char *sanitize(const char *in)
{
	char *ret = coerceToUtf8(in);
	return ret;
}
char *coerceToUtf8(const char *p)
{
	char *end;
	char *ret;
	int i;
	GString *coerced = g_string_new(NULL);

	while ( ! g_utf8_validate( p, -1, (const char **)&end ) ) {
		char buf[2]="";
		char *out;
		gsize r, w;
		GError *error;

		error = NULL;
		buf[0] =  0xff & *end;
		out = g_locale_to_utf8(buf, 1, &r, &w, &error);

		if(p != NULL) {
			g_string_append_len(coerced, p, end - p);
		}
		if(out != NULL) {
			g_string_append_len(coerced, out, w);
		}

		if(error) {
			g_error_free(error);
		}
		g_free(out);
		p = (char*)end;
		p++;
	}
	g_string_append(coerced, p);
	ret = g_string_free(coerced, FALSE);
	for(i=0;ret[i];i++) {
		//if(!isprint(ret[i])) {
		if(ret[i]<' ') {
			ret[i] = ' ';
		}
	}
	return ret;
}

